package com.rede.didiok.user.modulelist.community.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.rede.didiok.common.constant.conf.MessageConf;
import com.rede.didiok.common.constant.conf.SQLConf;
import com.rede.didiok.common.constant.conf.SysConf;
import com.rede.didiok.common.constant.global.Constants;
import com.rede.didiok.common.domainEvent.EventAction;
import com.rede.didiok.common.dto.ReportOption;
import com.rede.didiok.common.dto.community.CommunityReportDto;
import com.rede.didiok.common.dto.user.UserDto;
import com.rede.didiok.common.enums.AuditStatusEnum;
import com.rede.didiok.common.enums.StatusEnum;
import com.rede.didiok.common.exception.exceptionType.BusinessException;
import com.rede.didiok.common.exception.exceptionType.FeignException;
import com.rede.didiok.common.exception.exceptionType.QueryException;
import com.rede.didiok.common.feign.UserFeignClient;
import com.rede.didiok.common.holder.RequestHolder;
import com.rede.didiok.common.service.impl.SuperServiceImpl;
import com.rede.didiok.common.utils.R;
import com.rede.didiok.common.utils.ResultUtil;
import com.rede.didiok.common.utils.StringUtils;
import com.rede.didiok.common.utils.business.DomainEventUtil;
import com.rede.didiok.user.modulelist.community.dao.CommunityReportDao;
import com.rede.didiok.user.modulelist.community.entity.CommunityReportEntity;
import com.rede.didiok.user.modulelist.community.entity.MomentCommentEntity;
import com.rede.didiok.user.modulelist.community.entity.UserMomentEntity;
import com.rede.didiok.user.modulelist.community.service.CommunityReportService;
import com.rede.didiok.user.modulelist.community.service.MomentCommentService;
import com.rede.didiok.user.modulelist.community.service.UserMomentService;

/**
 * 举报服务 实体类服务实现类
 *
 * @author 遇见
 */
@Service
public class CommunityReportServiceImpl extends SuperServiceImpl<CommunityReportDao, CommunityReportEntity> implements CommunityReportService {

    @Resource
    CommunityReportDao communityReportDao;
    @Resource
    UserFeignClient userFeignClient;
    @Resource
    private DomainEventUtil domainEventUtil;

    @Resource
    MomentCommentService momentCommentService;
    @Resource
    UserMomentService userMomentService;

    @Override
    public IPage<CommunityReportEntity> getPageList(CommunityReportDto reportVO) {

        QueryWrapper<CommunityReportEntity> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotEmpty(reportVO.getContent())) {
            queryWrapper.eq(SQLConf.CONTENT, reportVO.getContent());
        }
        queryWrapper.eq(SQLConf.STATUS, StatusEnum.ENABLE);
        Page<CommunityReportEntity> page = new Page<>();
        page.setCurrent(reportVO.getCurrentPage());
        page.setSize(reportVO.getPageSize());
        queryWrapper.orderByDesc(SQLConf.CREATE_TIME);
        IPage<CommunityReportEntity> iPage = communityReportDao.selectPage(page, queryWrapper);
        List<String> userUidList = new ArrayList<>();
        iPage.getRecords().forEach(item -> {
            userUidList.add(item.getCreateUserUid());
            userUidList.add(item.getReportUserUid());
        });
        R userMapResult= userFeignClient.getUserAvatarMapByIds(userUidList);
        Map<String, UserDto> userMap = null;
        if (userMapResult.getCode() == 0) {
            userMap = userMapResult.getData(new TypeReference<Map<String, UserDto>>(){});
        }else {
            throw new FeignException("feign调用异常（获取带头像的用户Map）：失败");
        }
        Map<String, UserDto> finalUserMap = userMap;
        iPage.getRecords().forEach(item -> {
            item.setUser(finalUserMap.get(item.getCreateUserUid()));
            item.setReportUser(finalUserMap.get(item.getReportUserUid()));
        });

        return iPage;
    }

    @Override
    public String batchDeleteReport(List<String> reportUidList) {
        if (reportUidList.size() == 0) {
            return ResultUtil.errorWithMessage(MessageConf.PARAM_INCORRECT);
        }
        List<CommunityReportEntity> reportCollect = communityReportDao.selectBatchIds(reportUidList);
        reportCollect.forEach(item -> {
            item.setStatus(StatusEnum.DISABLED);
            domainEventUtil.publishEvent(EventAction.REPORT_DELETE, item);
        });

        return ResultUtil.successWithMessage(MessageConf.DELETE_SUCCESS);
    }


    @Override
    public Page<CommunityReportDto> queryList(ReportOption reportOption) {
        Page page = reportOption.creatPage();

        return communityReportDao.queryList(page, reportOption);
    }

    @Override
    public Page<CommunityReportDto> queryListByContent(ReportOption reportOption) {
        Page page = reportOption.creatPage();
        return communityReportDao.queryListByContent(page, reportOption);
    }

    @Override
    public CommunityReportDto submitReport(ReportOption reportOption) {
        /**
         * 当前举报人
         */
        String userUid = RequestHolder.getUserUid();
        if (StringUtils.isEmpty(userUid)) {
            throw new QueryException(Constants.USER_UID_EMPTY);
        }
        ReportType type = ReportType.getType(reportOption.getReportType());
        if (type == null) {
            throw new BusinessException("举报类型错误,请重新核对后举报");
        }
        String reportUserId = null;
        /**
         * 根据类型获取举报的内容
         */
        switch (type) {

            case COMMENT:
                MomentCommentEntity comment = momentCommentService.getOne(
                        new LambdaQueryWrapper<MomentCommentEntity>()
                                .eq(MomentCommentEntity::getUid, reportOption.getReportContentUid())
                                .eq(MomentCommentEntity::getStatus, StatusEnum.ENABLE)
                );
                if (comment == null) {
                    throw new BusinessException("该评论已被删除,感谢您为了社区健康环境做出贡献!");
                }
                // 检查该资源是否被举报过
                checkReportStatus(userUid, comment.getUid(), type.getKey());
                reportUserId = comment.getCreateUserUid();
                break;
            case MOMENT:
                UserMomentEntity userMoment = userMomentService.getOne(
                        new LambdaQueryWrapper<UserMomentEntity>()
                                .eq(UserMomentEntity::getUid, reportOption.getReportContentUid())
                                .eq(UserMomentEntity::getStatus, StatusEnum.ENABLE)
                );
                if (userMoment == null) {
                    throw new BusinessException("该动态已被删除,感谢您为了社区健康环境做出贡献!");
                }
                // 检查该资源是否被举报过
                checkReportStatus(userUid, userMoment.getUid(), type.getKey());
                reportUserId = userMoment.getCreateUserUid();
                break;
            default:
                throw new BusinessException("举报类型错误,请重新核对后举报");
        }
        CommunityReportEntity report = new CommunityReportEntity();
        BeanUtils.copyProperties(reportOption, report, SysConf.STATUS);
        report.setReportUserUid(reportUserId);
        report.setCreateUserUid(userUid);
        boolean isSave = this.save(report);
        // 发送举报领域事件
        if (isSave) {
            domainEventUtil.publishEvent(EventAction.REPORT_ADD, report);
        }
        /**
         * 下架操作
         */
        if (beyondTime(reportOption.getReportContentUid(), type)) {
            this.revocationContent(reportOption.getReportContentUid(), type);
        }
        CommunityReportDto reportVO = new CommunityReportDto();
        BeanUtils.copyProperties(report, reportVO);
        return reportVO;
    }

    @Override
    public String handleReport(String reportContentUid, String reportType, String reportUid, Integer progress) {
        List<CommunityReportEntity> reportList = this.list(
                new LambdaQueryWrapper<CommunityReportEntity>()
                        .eq(CommunityReportEntity::getProgress, 0)
                        .eq(reportContentUid != null, CommunityReportEntity::getReportContentUid, reportContentUid)
                        .eq(CommunityReportEntity::getReportType, reportType)
                        .eq(reportUid != null, CommunityReportEntity::getUid, reportUid)
        );


        if (reportList == null || reportList.size() == 0) {
            throw new BusinessException("未查询到该内容的举报信息");
        }


        reportList.forEach(item -> {
            item.setProgress(progress);
            domainEventUtil.publishEvent(EventAction.REPORT_HANDLE, item);
        });
        boolean isSave = this.updateBatchById(reportList);

        for (int i = reportList.size() - 1; i >= 0; i--) {
            CommunityReportEntity report = reportList.get(i);
            /**
             * progress 值
             * 1为已查看 不做处理
             * 2为已处理
             */
            switch (progress) {
                case 1:
                    /**
                     *
                     * 暂不做处理
                     */
                    break;
                case 2:
                    /**
                     * 下架处理
                     */
                    this.revocationContent(report.getReportContentUid(), ReportType.getType(report.getReportType()));
                    break;
                default:
                    break;
            }
        }

        return ResultUtil.successWithMessage("处理成功");
    }

    /**
     * 检测举报次数是否超出阈值
     *
     * @param reportUid
     * @param reportType
     * @return
     */
    public Boolean beyondTime(String reportUid, ReportType reportType) {
        Integer count = this.count(
                new LambdaQueryWrapper<CommunityReportEntity>()
                        .eq(CommunityReportEntity::getReportContentUid, reportUid)
                        .eq(CommunityReportEntity::getReportType, reportType.getKey())
        );
        //todo 写死为10 后续加入举报限制配置  读取配置
        /**
         * count+1>=10
         * 新增前检测 所以是 默认count多+1
         */
        return count >= 10 ? true : false;
    }

    /**
     * 下架操作
     */
    public Boolean revocationContent(String reportUid, ReportType reportType) {
        Boolean update = true;
        switch (reportType) {

            case COMMENT:
                MomentCommentEntity comment = momentCommentService.getById(reportUid);
                if (comment == null) {
                    return false;
                }
                comment.setRejectReason("因举报过多暂时下架");
                comment.setAuditStatus(AuditStatusEnum.REJECT);
                update = comment.updateById();
                break;
            case MOMENT:
                UserMomentEntity userMoment = userMomentService.getById(reportUid);
                if (userMoment == null) {
                    return false;
                }
                userMoment.setRejectReason("因举报过多暂时下架");
                userMoment.setAuditStatus(AuditStatusEnum.REJECT);
                update = userMoment.updateById();

                break;

        }
        return update;
    }

    /**
     * 检查该资源是否被举报过
     *
     * @param userUid
     * @param reportContentUid
     * @param reportType
     * @return
     */
    private void checkReportStatus(String userUid, String reportContentUid, String reportType) {
        Integer count = this.count(new LambdaQueryWrapper<CommunityReportEntity>()
                .eq(CommunityReportEntity::getCreateUserUid, userUid)
                .eq(CommunityReportEntity::getReportContentUid, reportContentUid)
                .eq(CommunityReportEntity::getReportType, reportType)
                .eq(CommunityReportEntity::getStatus, StatusEnum.ENABLE)
        );
        if (count > 0) {
            throw new QueryException("你已举报过，请勿重复操作");
        }
    }


    public enum ReportType {
        COMMENT("comment"),
        MOMENT("moment");

        private String key;

        ReportType(String key) {
            this.key = key;
        }

        public static ReportType getType(String key) {
            for (ReportType info : ReportType.values()) {
                if (info.getKey().equals(key)) {
                    return info;
                }
            }
            return null;
        }

        public String getKey() {
            return this.key;
        }

    }
}
